package org.jflame.commons.crypto;

import java.security.SecureRandom;

import javax.crypto.Cipher;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

/**
 * SM2非对称加密工具类.基于bcprov-jdk15on(&gt;1.61)
 */
public class SM2Encryptor extends AsymmetricEncryptor {

    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(),
            x9ECParameters.getG(), x9ECParameters.getN());

    private SM2Engine engine;

    public SM2Encryptor() throws EncryptException {
        this(SM2Engine.Mode.C1C3C2);
    }

    public SM2Encryptor(SM2Engine.Mode engineMode) throws EncryptException {
        super(Transformation.SM2_NONE_NOPADDING);
        engine = new SM2Engine(engineMode);
    }

    @Override
    public byte[] doProcess(byte[] data, CipherArgs param, int mode) throws EncryptException {
        if (Cipher.ENCRYPT_MODE == mode) {
            BCECPublicKey localECPublicKey = (BCECPublicKey) param.getKey(Cipher.ENCRYPT_MODE);
            ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(),
                    ecDomainParameters);
            engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
        } else {
            BCECPrivateKey localECPrivateKey = (BCECPrivateKey) param.getKey(Cipher.DECRYPT_MODE);
            ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(),
                    ecDomainParameters);
            engine.init(false, ecPrivateKeyParameters);
        }
        try {
            return engine.processBlock(data, 0, data.length);
        } catch (InvalidCipherTextException e) {
            throw new EncryptException(mode == Cipher.ENCRYPT_MODE ? "SM2加密异常" : "SM2解密异常", e);
        }
    }

    @Override
    public boolean isSupport() {
        return transformation.getAlg() == Alg.SM2;
    }

}
